/*
 *  Brno University of Technology
 *
 *  Copyright (C) 2009-2010 by Brno University of Technology and the contributors
 *
 *  Complete list of developers available at our web site:
 *
 *       http://spl.utko.feec.vutbr.cz
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package cz.vutbr.feec.utko.ef.evolution;

import org.apache.log4j.Logger;

import cz.vutbr.feec.utko.ef.core.Config;
import cz.vutbr.feec.utko.ef.core.Population;
import cz.vutbr.feec.utko.ef.evolution.stats.DefaultCrossoverAndMutationRateManager;
import cz.vutbr.feec.utko.ef.individuals.MutationIsNotPossibleException;

/**
 * The Class EvolutionSpecifierAdapter performs sequence of reproductions,
 * crossovers, mutations and selection operations.
 */
abstract public class EvolutionSpecifierAdapter implements IEvolutionSpecifier {
	
	/** The log. */
	private static Logger log = Logger
			.getLogger(EvolutionSpecifierAdapter.class);
	
	/** The log fitness. */
	private static Logger logFitness = Logger.getLogger("evolution");
	
	/** The num. */
	private int num = 0;
	
	/** The config. */
	private Config config;

	/**
	 * Gets the config.
	 * 
	 * @return the config
	 */
	protected Config getConfig() {
		return config;
	}

	/**
	 * Instantiates a new evolution specifier adapter.
	 * 
	 * @param cmRateManager the cm rate manager
	 * @param config the config
	 */
	public EvolutionSpecifierAdapter(
			DefaultCrossoverAndMutationRateManager cmRateManager, Config config) {
		logFitness.info("INIT");
		this.config = config;
		this.cmRateManager = cmRateManager;
	}

	/**
	 * Crossover all individuals in population p.
	 * 
	 * @param cfg
	 *            the configuration
	 * @param p
	 *            the population
	 * 
	 * @return the crossovered individuals (not including original population
	 *         members)
	 */
	abstract public Population crossoverAll(Config cfg, Population p);

	/** The cm rate manager. */
	private final DefaultCrossoverAndMutationRateManager cmRateManager;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cz.vutbr.feec.utko.ef.evolution.IEvolutionSpecifier#evolve(cz.vutbr.feec
	 * .utko.ef.core.Config, cz.vutbr.feec.utko.ef.core.Population)
	 */
	@Override
	public Population evolve(Config cfg, Population p) {
		long evolutionProcessingTime = System.currentTimeMillis();

		p = sortAndSelectBestChromozomes(cfg, p);
		Chromozome best = p.getBestIndividual().cloneDeep();
		best.validate();
		
		p.setPopulationStatisticData(best,p,cfg);
		
		for (int i = 0; i < p.size(); i++) {
			p.getIndividual(i).validate();
		}

		Population crossovered = crossoverAll(cfg, p);
		p.add(crossovered);

		for (int i = 0; i < p.size(); i++) {
			assert p.getIndividual(i).getCachedFitness() != null;
		}

		Population mutated = mutateAll(cfg, p);
		p.add(mutated);
		
		p.add(best);

		p.addEvolutionStats(evolutionProcessingTime, cfg);

		log.debug("MUT " + cfg.getMutationRate() + " XOVER "
				+ cfg.getCrossoverRate());

		p = sortAndSelectBestChromozomes(cfg, p);
		
		logFitness.info("Fitness:" + best.getCachedFitness());

		log.debug("FITNESS OF BEST CHROMOZOME: " + best.getCachedFitness()
				+ " in evolution " + num++);
		log.debug("----------------------- Evolution end - population size "
				+ p.size() + " ----------------------");
		return p;
	}

	public abstract double getCrossoverSuccessRate();

	public abstract double getMutationSuccessRate();

	/**
	 * Mutate all chromozomes in the population p.
	 * 
	 * @param cfg
	 *            the config
	 * @param p
	 *            the population
	 * 
	 * @return the population
	 * 
	 * @throws MutationIsNotPossibleException
	 */
	abstract public Population mutateAll(Config cfg, Population p);

	/**
	 * Removes the duplicate.
	 * 
	 * @param p
	 *            the population
	 * 
	 * @return the population
	 */
	public abstract Population removeDuplicate(Population p);

	/**
	 * Sort and select best chromozomes.
	 * 
	 * @param cfg
	 *            the config
	 * @param p
	 *            the population
	 * 
	 * @return the population
	 */
	abstract public Population sortAndSelectBestChromozomes(Config cfg,
			Population p);
	
	/**
	 * Gets the fitness diversity.
	 * 
	 * @param p the p
	 * 
	 * @return the fitness diversity
	 */
	private double getFitnessDiversity(Population p) {
		double mean = 0;
		for (int i = 0; i < p.size(); i++) {
			mean += p.getIndividual(i).getCachedFitness();
		}

		mean = mean / (double) p.size();

		double diversity = 0;
		for (Chromozome t : p) {
			double div = Math.abs((mean - t.getCachedFitness()));
			diversity += div;
		}
		return diversity / (double) p.size();
	}

	/**
	 * Gets the mean fitness.
	 * 
	 * @param p the p
	 * 
	 * @return the mean fitness
	 */
	private double getMeanFitness(Population p) {
		double mean = 0;
		for (int i = 0; i < p.size(); i++) {
			mean += p.getIndividual(i).getCachedFitness();
		}
		return mean / (double) p.size();
	}
}
